在本系列文中,所有的程式碼以及測試都可以在 should-i-use-fp-ts 找到,今日的範例放在 src/day-17
並且有習題和測試可以讓大家練習。
今天我們來看看平常寫業務邏輯的時候比較常使用 Option
的部分。
使用 Option
的情境通常會滿足兩項條件。
validation
,先來看非 Option
實作,這個情境為 user
的 validate,需滿足所有條件才會回傳 user
。type User = {
username: string;
email: string;
password: string;
};
const moreThan3Chars = (xs: string): string | null =>
xs.length > 3 ? xs : null;
const validateEmail = (email: string): string | null =>
email.includes('@') ? email : null;
const moreThan6Chars = (xs: string): string | null =>
xs.length > 6 ? xs : null;
const hasCapital = (xs: string): string | null =>
/[A-Z]/.test(xs) ? xs : null;
const hasNumber = (xs: string): string | null =>
/\d/.test(xs) ? xs : null;
const validateOriginal = (user: User) => {
if (!moreThan3Chars(user.username)) return null;
if (!validateEmail(user.email)) return null;
if (!moreThan6Chars(user.password)) return null;
if (!hasCapital(user.password)) return null;
if (!hasNumber(user.password)) return null;
return { username: user.username, email: user.email, password: user.password };
};
接下來展示 Option
的實作。
const moreThan3CharsOption = O.fromPredicate((xs: string) =>
xs.length > 3);
const validateEmailOption = O.fromPredicate((xs: string) =>
xs.includes('@'));
const moreThan6CharsOption = O.fromPredicate((xs: string) =>
xs.length > 6);
const hasCapitalOption = O.fromPredicate((xs: string) =>
/[A-Z]/.test(xs));
const hasNumberOption = O.fromPredicate((xs: string) =>
/\d/.test(xs));
export const validateOption = ({ username, email, password }: User) => pipe(
username,
moreThan3CharsOption,
O.bindTo('username'),
O.bind('email', () => validateEmailOption(email)),
O.bind('password', () => pipe(
password,
moreThan6CharsOption,
O.chain(hasCapitalOption),
O.chain(hasNumberOption),
)),
O.getOrElseW(() => null),
);
大家會比較喜歡哪種形式呢?
再來,或許有人會說:"沒有客製化錯誤訊息我怎麼知道錯在哪裡?"。沒關係,等明天開始學習 Either
,我們就可以針對失敗的情境添加訊息。
今天的主題在 should-i-use-fp-ts src/day-17
有習題和測試可以練習,大家可以嘗試自己能不能寫出自己的 validate。